#ifndef SERIALPORT_H
#define SERIALPORT_H

#if defined(OS_WIN) || defined(OS_LINUX)

#else

#if defined(_WIN32) || defined(__CYGWIN__)
#define OS_WIN
#endif
#if defined(__GNUC__) || defined(__GNUG__)
#define OS_LINUX
#endif

#endif

#ifdef BUILDDLL
#if defined(OS_WIN)
#define MODBUS_DEV_LIBDLL __declspec(dllexport)
#elif defined(OS_LINUX)
#define MODBUS_DEV_LIBDLL __attribute__((visibility("default")))
#else
#define MODBUS_DEV_LIBDLL
//__declspec(dllexport)
#endif
#else
#if defined(OS_WIN)
#define MODBUS_DEV_LIBDLL __declspec(dllimport)
#elif defined(OS_LINUX)
#define MODBUS_DEV_LIBDLL
#else
#define MODBUS_DEV_LIBDLL
//__declspec(dllimport)
#endif
#endif

/**
 * @brief 串口操作类
 *
 */
class SerialPort {
public:
    static void getAvailableSerialPorts(char * toBuf);
    SerialPort();
    SerialPort(const SerialPort& obj);
    SerialPort& operator=(const SerialPort& obj);
    ~SerialPort();

    enum BaudRate {
        Baud1200 = 1200,
        Baud2400 = 2400,
        Baud4800 = 4800,
        Baud9600 = 9600,
        Baud19200 = 19200,
        Baud38400 = 38400,
        Baud57600 = 57600,
        Baud115200 = 115200,
        UnknownBaud = -1
    };

    enum DataBits {
        Data5 = 5,
        Data6 = 6,
        Data7 = 7,
        Data8 = 8,
        UnknownDataBits = -1
    };

    enum Parity {
        NoParity = 0,
        EvenParity = 2,
        OddParity = 3,
        SpaceParity = 4,
        MarkParity = 5,
        UnknownParity = -1
    };

    enum StopBits {
        OneStop = 1,
        OneAndHalfStop = 3,
        TwoStop = 2,
        UnknownStopBits = -1
    };

    enum FlowControl {
        NoFlowControl,
        HardwareControl, // RTS/CTS
        SoftwareControl, // XON/XOFF
        UnknownFlowControl = -1
    };

    /**
     * @brief 打开串口
     *
     * @return true 成功
     * @return false 失败
     */
    bool open();
    /**
     * @brief 查询串口是否打开
     *
     * @return true 串口打开了
     * @return false 串口未打开
     */
    bool isOpen();
    /**
     * @brief 关闭串口
     *
     */
    void close();

    /**
     * @brief 设定串口名
     *
     * @param name 串口名
     */
    void setPortName(const char* name);
    /**
     * @brief 设定串口波特率
     *
     * @param baudRate 串口波特率
     * @return true 成功
     * @return false 失败
     */
    bool setBaudRate(int baudRate);
    /**
     * @brief 设定串口检验方式
     *
     * @param parity 串口检验方式
     * @return true 成功
     * @return false 失败
     */
    bool setParity(Parity parity);
    /**
     * @brief 设定串口数据位数
     *
     * @param dataBits 串口数据位数
     * @return true 成功
     * @return false 失败
     */
    bool setDataBits(DataBits dataBits);
    /**
     * @brief 设定串口停子位位数
     *
     * @param stopBits 串口停子位位数
     * @return true 成功
     * @return false 失败
     */
    bool setStopBits(StopBits stopBits);
    /**
     * @brief 设定串口流控制方式
     *
     * @param flowControl 串口流控制方式
     * @return true 成功
     * @return false 失败
     */
    bool setFlowControl(FlowControl flowControl);

#ifdef OS_WIN
    /**
     * @brief 设置串口通信超时参数，全部单位都为毫秒
     *
     * @param rInterval 读间隔超时
     * @param rMultiple 读时间系数
     * @param rConst 读时间常量
     * @param wMultiple 写时间系数
     * @param wConst 写时间常量
     * @return true 成功
     * @return false 失败
     */
    bool setTimeoutParams(int rInterval, int rMultiple, int rConst,
        int wMultiple, int wConst);
#endif
    /**
     * @brief 设置串口读取系统缓冲区大小
     *
     * @param size 缓冲区大小, 单位字节
     */
    void setReadBufferSize(unsigned int size);
    /**
     * @brief 设置串口写入系统缓冲区大小
     *
     * @param size 缓冲区大小, 单位字节
     */
    void setWriteBufferSize(unsigned int size);
    /**
     * @brief 立即将缓冲区内容写到串口中
     *
     * @return true 成功
     * @return false 失败
     */
    bool flush();
    /**
     * @brief 清除串口读写缓冲区
     *
     * @return true 成功
     * @return false 失败
     */
    bool clear();
    /**
     * @brief 等待读取直到超时
     *
     * @param timeout_ms 超时时长
     * @return true 成功
     * @return false 失败
     */
    bool waitForReadyRead(int timeout_ms);

    /**
     * @brief 读串口，返回实际读取的字节数量，失败返回-1
     *
     * @param buf 读取放置的缓冲区指针，由调用方申请和释放
     * @param bytesToRead 要读取的字节数量
     * @return int 返回读取到的字节数量, 失败返回-1
     */
    int read(void* buf, int bytesToRead);

    /**
     * @brief 写串口，返回实际写入的字节数量，失败返回-1
     *
     * @param buf 需要写入数据的缓冲区指针，由调用方申请和释放
     * @param bytesToWrite 要写入的字节数量
     * @return int 返回写入了的字节数量, 失败返回-1
     */
    int write(void* buf, int bytesToWrite);

    /**
     * @brief 写串口，返回实际写入的字节数量，失败返回-1
     *
     * @param buf 需要写入数据的缓冲区指针，由调用方申请和释放
     * @param bytesToWrite 要写入的字节数量
     * @return int 返回写入了的字节数量, 失败返回-1
     */
    int write(const void* buf, int bytesToWrite);

    /**
     * @brief 获取最后的错误标记
     *
     * @return int 错误标记
     */
    int getLastError();

    /**
     * @brief 清除串口错误
     *
     */
    void clearSerialPortError();

private:
    /**
     * @brief 设置默认的串口参数
     *
     */
    void setDefaultParams();

    /**
     * @brief 更新设定的串口参数
     *
     * @return true 成功
     * @return false 失败
     */
    bool updateParamToSerialPort();

    char* m_portName;
    int m_baudRate;
    Parity m_parity;
    DataBits m_dataBits;
    StopBits m_stopBits;
    FlowControl m_flowControl;

    unsigned int m_readBufSize;
    unsigned int m_writeBufSize;

    bool m_openPortFailed;
    void* m_portHandle;
    
    int m_lastErr;
};

#endif // SERIALPORT_H
